package net.esj.basic.core.engine.support.message.context;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import net.esj.basic.core.engine.resource.ApplicationResourceBundler;
import net.esj.basic.core.engine.resource.ResourceBundler;
import net.esj.basic.core.engine.support.message.AbstractMessageProvider;

public class ResourceBundleMessageProvider extends AbstractMessageProvider {

	private final Map<String, ResourceBundler> cachedResourceBundler = new HashMap<String, ResourceBundler>();

	private final Map<ResourceBundle, Map<String, Map<Locale, MessageFormat>>> cachedBundleMessageFormats =
		new HashMap<ResourceBundle, Map<String, Map<Locale, MessageFormat>>>();

	
	@Override
	protected MessageFormat resolveCode(String sourceName, String code,
			Locale locale) {
		synchronized (this.cachedResourceBundler) {
			ResourceBundler resourceBundler = cachedResourceBundler
					.get(sourceName);
			if(resourceBundler!=null){
				ResourceBundle rb = resourceBundler.getResourceBundle(locale);
				return getMessageFormat(rb, code, locale);
			}else{
				resourceBundler = new ApplicationResourceBundler(sourceName);
				ResourceBundle rb = resourceBundler.getResourceBundle(locale);
				return getMessageFormat(rb, code, locale);
			}
		}
	}

	protected MessageFormat getMessageFormat(ResourceBundle bundle,
			String code, Locale locale) throws MissingResourceException {

		synchronized (this.cachedBundleMessageFormats) {
			Map<String, Map<Locale, MessageFormat>> codeMap = this.cachedBundleMessageFormats
					.get(bundle);
			Map<Locale, MessageFormat> localeMap = null;
			if (codeMap != null) {
				localeMap = codeMap.get(code);
				if (localeMap != null) {
					MessageFormat result = localeMap.get(locale);
					if (result != null) {
						return result;
					}
				}
			}

			String msg = code;
			if(bundle!=null){
				msg = getStringOrNull(bundle, code);
			}
			if (msg != null) {
				if (codeMap == null) {
					codeMap = new HashMap<String, Map<Locale, MessageFormat>>();
					this.cachedBundleMessageFormats.put(bundle, codeMap);
				}
				if (localeMap == null) {
					localeMap = new HashMap<Locale, MessageFormat>();
					codeMap.put(code, localeMap);
				}
				MessageFormat result = createMessageFormat(msg, locale);
				localeMap.put(locale, result);
				return result;
			}

			return null;
		}
	}
	
	private String getStringOrNull(ResourceBundle bundle, String key) {
		try {
			return bundle.getString(key);
		}
		catch (MissingResourceException ex) {
			return null;
		}
	}

	@Override
	public int order() {
		return Integer.MAX_VALUE;
	}
}
